package p2pproxy;

import java.util.LinkedList;
import java.util.List;

import nfwx.analytics.BuildConfig;


public class P2PSClient {	
	static public class DataItem {
		public byte [] data;
		public int size;
		public int type;
		public int dts;
		public int pts;
		public DataItem() {
			data = new byte[1024*1024];
			size = 0;
			type = RawDataType.fileBody.ordinal();
		}
		public DataItem(int bufSize, int type)
		{
			data = new byte[bufSize];
			size = 0;
			this.type = type;
		}
	}

	///视频格式	
	///自动按url后缀检测视频格式
	public static final int formatAuto=0;
	///FPV格式
	public static final int formatFPV=1;
	///FLV格式
	public static final int formatFLV=2;
	///MP4格式
	public static final int formatMP4=3;
	///TS格式
	public static final int formatTS=4;
	///HLS格式
	public static final int formatHLS=5;
	///VMS格式
	public static final int formatVMS=6;
	
	
	public enum RawDataType {
		videoMime, ///< 视频MIME, 仅在raw格式时有效
		audioMime, ///< 音频MIME, 仅在raw格式时有效
		videoCfgData, ///< 视频配置帧, 仅在raw格式时有效
		audioCfgData, ///< 音频配置帧, 仅在raw格式时有效
		videoFrame, ///< 视频帧, 仅在raw格式时有效
		audioFrame, ///< 音频帧, 仅在raw格式时有效
		fileHeader, ///< 文件头, 仅在非raw格式时有效
		fileBody, ///< 文件体 , 仅在非raw格式时有效
		fileTailer ///< 文件尾
	};
	
	private DataItem _data = new DataItem();
	///空闲DataItem列表, 分为10个级别256, 512, 1K, 2K, 4K, 8K, 16K, 32K, 64K, 128K 
	private  List<DataItem>[] _freeLists = new List[10]; 
	private long _handle;
	
	public P2PSClient()
	{
		for(int i=0; i<10; ++i) {
			_freeLists[i] = new LinkedList<DataItem>();
		}
	}
	
	private int _calcSizeLevelToGet(int size)
	{
		int orgSize=256;
		for(int i=0; i<10; ++i) {
			if (size <= orgSize) return i;
			orgSize <<= 1;
		}
		return -1;
	}
	
	private int _calcSizeLevelToRelease(int size)
	{
		int orgSize=256*1024;
		if (size >= orgSize) return -1;
		orgSize >>= 1;
		for(int i=9; i>=0; --i) {
			if (size >= orgSize) return i;
			orgSize >>= 1;
		}
		return -1;
	}
	///获取一个空闲DataItem, it.data的长度未必满足bufSize, 如果不满足需要外部重新分配 
	private DataItem _getDataItem(int bufSize, int type)
	{
		int l = _calcSizeLevelToGet(bufSize);
		if (l == -1) {
			return new DataItem(bufSize, type);
		}
		LinkedList<DataItem> list = (LinkedList<DataItem>)_freeLists[l];
		synchronized(this) {
			if (list.isEmpty()) {
				return new DataItem(bufSize, type);
			} else {
				DataItem it = list.get(0);
				it.size = 0;
				it.type = type;
				list.remove(0);
				return it;
			}
		}
	}	
	///释放DataItem到空闲队列, 仅在用于getRaw函数返回的DataItem, get/get2返回的不要调用此函数
	public void releaseDataItem(DataItem it)
	{
		int l = _calcSizeLevelToRelease(it.data.length);
		if (l == -1) return;
		LinkedList<DataItem> list = (LinkedList<DataItem>)_freeLists[l];
		int itsize = (int)Math.pow(2, l+8);
		synchronized(this) {
			if (list.size() * itsize > 1*1024*1024) return; //每个队列最大1M
			list.add(it);
		}
	}
	
	public void freeAllDataItems()
	{
		synchronized(this) {
			for(int i=0; i<10; ++i) {
				_freeLists[i].clear();
			}
		}
	}
	
	/*!
	@brief 初始化P2PSClient对象
	@param context android app 的上下文对象
	@param p2pMgr P2P管理器, 如果传入null则自动创建P2P管理器
	@param outputType 输出类型, 参数取值为flv: 输出FLV, ts: 输出TS格式, raw: 输出原始帧, tsserver: 以HTTP服务器方式输出TS格式
	*/
	public native boolean open(Object context, P2PSManager p2pMgr, String outputType);
	/*!
	@brief 添加视频文件到播放器
	@param urls 视频文件URL，可以为url格式或者为JSON格式，如果是JSON格式则要求格式为 [{"url": 视频播放地址, "backup": true/false 是否为备份播放地址}, ... ]
	@param streamers 视频串流地址 可以为url格式或者为JSON格式，如果是JSON格式则要求格式为 [{"url": 视频串流地址, "backup": true/false 是否为备份串流地址}, ... ], 如果没有串流地址设置为null
	@param opts 视频选项, JSON格式，如果没有视频选项，传入null, 目前支持的视频选项有:
			- encrypt_code 视频加密密钥，如果不传则自动根据要播放的视频和服务器类型自动向服务器取得密钥
			- buffer_length 播放缓冲区长度，默认为10000, 即10秒
			- extra 跟该视频绑定的附加参数，要求为字符串格式
			- owner_uid 视频所属用户uid, 如果是VMS视频格式，该参数自动从VMS query时取得
	@param start 播放时间点ms
	@param duration 播放时长ms, 如果为-1则到文件尾
	@param index 添加点[0..fileCount]之间，如果为-1则加在尾部
	@param format 视频格式
	@return 是否添加文件成功
	*/
	public native boolean addFile(String urls, String streamers, String opts, int start, int duration, int index, int format);
	/*!
	@brief 移除视频文件
	@param index 文件索引
	*/
	public native boolean removeFile(int index);
	///获取视频文件数量
	public native int fileCount();
	///清除所有视频文件
	public native void clear();
	///视频文件是否已经加载好准备播放
	public native boolean fileReady(int index);
	///当前播放的视频是否发生错误
	public native boolean error();
	///视频是否准备好播放
	public native boolean ready();
	///返回播放器端口, 仅在outputType == "tsserver"时该函数有效，当播放器未准备好时返回0, 准备好后返回一个TCP 端口，通过访问http://127.0.0.1:port/live.ts 可以播放TS格式的视频流, 访问http://127.0.0.1:port/live.m3u8可以播放HLS格式的视频流
	public native int port();
	///执行播放
	public native void play();
	///执行暂停
	public native void pause();
	///执行定位操作
	public native boolean seek(int t);
	///定位到最临近的关键帧
	public native boolean seekToKeyFrame(int t);
	///定位到时间码
	public native boolean seekByTimeCode(double tc, boolean keyFrameAccuracy, boolean mustMatch, boolean asc);
	/*!
	@brief 从播放器中取出视频数据
	@note 如果outputType == "tsserver" 则该函数一律返回null
	*/
	public native byte[] get();
	/*!
	@brief 从播放器中取出视频数据
	@note 如果outputType == "tsserver" 则该it.size 一律为0
	*/
	public native void fill(DataItem it);
	/*!
	@brief 从播放器中取出视频数据
	@note 如果outputType == "tsserver" 则该DataItem.size 一律为0
	*/
	public DataItem get2() {
		fill(_data);
		return _data;
	}	
	
	///获取原始数据帧
	public native int getRaw(List<DataItem> items, int duration);	
	
	///获取当前播放文件的vsid
	public native String getCurrentPlayVsid();

	///获取播放文件时间信息
	public native boolean getFileTimeInfo(int fileIdx, FileTimeInfo info);
	///播放是否结束
	public native boolean stopped();
	///所有视频的播放时长
	public native int duration();
	///当前视频是否是直播
	public native boolean isLive();	
	///关闭播放器，其它所有函数均不要再调用
	public native void close();	
	///当前直播视频是否无信号
	public native boolean noSignal();
	///获取状态JSON格式
	public native String getStatus();
	/*!
	@brief 获取当前播放器输出的视频数据的时间码
	@note 当使用外部播放器播放tsserver的流时，tsserver本身会缓存数据，外部播放器也会缓存数据，因此要精准确定外部播放器当前播放点的时间码需要调用getOutSidePlayerTimeCode来实现
	@return 当前播放器输出时间码，单位ms, 如果时间码不可用或者尚不可用返回0
	*/
	public native double getTimeCode();
	/*!
	@brief 获取当前播放器输出的视频数据的时间
	@note 当使用外部播放器播放tsserver的流时，tsserver本身会缓存数据，外部播放器也会缓存数据，因此要精准确定外部播放器当前播放点的时间需要调用getOutSidePlayerStreamTime来实现
	@return 当前播放器输出时间，单位ms
	*/
	public native int getTime();
	/*!
	@brief 获取外部播放器的当前流时间
	@param outsideTime 外部播放器的当前时间
	@return 映射到视频流上的时间, 单位ms
	*/
	public native int getOutSidePlayerStreamTime(int outsideTime);
	/*!
	@brief 获取外部播放器的当前流时间码
	@param outsideTime 外部播放器的当前时间
	@return 映射到视频流上的时间码, 单位ms, 如果时间码不可用或者尚不可用返回0
	*/
	public native double getOutSidePlayerTimeCode(int outsideTime);
	/*!
	@brief 目前播放器是否处于时移模式
	@return 0 表示不支持时移 1 表示处于时移模式 2 表示支持时移但是目前不在时移模式
	*/
	public native int inTimeShiftMode();
	/*!
	 @brief 底层数据是否在缓冲中
	 */
	public native boolean bufferring();
	
	static {
		try {
			//System.loadLibrary("p2psclient");
			System.loadLibrary(BuildConfig.SO_LIB);
		} catch(Error e) {
			e.printStackTrace();
		}
	}	
}
